# -*- coding: utf-8 -*-
"""
Created on Fri Oct 22 08:33:30 2021

@author: ASUS
"""
#实验一
#用numpy 的dot 函数将两个向量相乘
import numpy as np
example_input = [1, .2, .1, .05, .2]
example_weights = [.2, .12, .4, .6, .90]
input_vector = np.array(example_input)
weights = np.array(example_weights)
bias_weight = .2
activation_level = np.dot(input_vector, weights) +\
    (bias_weight * 1)
#bias_weight * 1 只是为了强调bias_weight 和其他权重一样：权重与输入值相乘，区别只是bias_weight的输入特征值总是1
activation_level

#假设选择一个简单的阈值激活函数，并选择0.5 作为阈值，结果如下：
threshold = 0.5
if activation_level >= threshold:
    perceptron_output = 1
else:
    perceptron_output = 0
perceptron_output
#对于给定的输入样本example_input 和权重，这个感知机将会输出1。
#如果有许多example_input 向量，输出将会是一个标签集合。

expected_output = 0
new_weights = []
for i, x in enumerate(example_input):
    new_weights.append(weights[i] + (expected_output -\
    perceptron_output) * x)
#在上述的第一次计算中，new_weight = 0.2 + (0 - 1) × 1 = −0.8
weights = np.array(new_weights)
example_weights
#初始权重
#[0.2, 0.12, 0.4, 0.6, 0.9]
weights
#新的权重
#[-0.8 -0.08 0.3 0.55 0.7]

sample_data = [[0, 0], # False, False
    [0, 1], # False, True
    [1, 0], # True, False
    [1, 1]] # True, True

expected_results = [0, # (False OR False) gives False
    1, # (False OR True ) gives True
    1, # (True OR False) gives True
    1] # (True OR True ) gives True
activation_threshold = 0.5

#numpy 可以用来做向量（数组）乘法
#random 用来初始化权重
from random import random
import numpy as np
weights = np.random.random(2)/1000 # Small random float 0 < w < .001
weights
#偏置
bias_weight = np.random.random() / 1000
bias_weight

#感知机随机预测
for idx, sample in enumerate(sample_data):
    input_vector = np.array(sample)
    activation_level = np.dot(input_vector, weights) +\
    (bias_weight * 1)
    if activation_level > activation_threshold:
        perceptron_output = 1
    else:
        perceptron_output = 0
    print('Predicted {}'.format(perceptron_output))
    print('Expected: {}'.format(expected_results[idx]))
    print()
    
#感知机学习
#-------------------------------
for iteration_num in range(5):
    correct_answers = 0
    for idx, sample in enumerate(sample_data):
        input_vector = np.array(sample)
        weights = np.array(weights)
        activation_level = np.dot(input_vector, weights) + (bias_weight * 1)
        if activation_level > activation_threshold:
            perceptron_output = 1
        else:
            perceptron_output = 0
        if perceptron_output == expected_results[idx]:
            correct_answers += 1
        new_weights = []
        for i, x in enumerate(sample):
            new_weights.append(weights[i] + (expected_results[idx] - perceptron_output) * x)
        #偏置权重也会随着输入一起更新
        bias_weight = bias_weight + ((expected_results[idx] - perceptron_output) * 1)
        weights = np.array(new_weights)
    print('{} correct answers out of 4, for iteration {}'.format(correct_answers, iteration_num)) 
#实验二
import numpy as np
from keras.models import Sequential
#Kera 的基础模型类
from keras.layers import Dense, Activation
#Dense 是神经元的全连接层
from tensorflow.keras.optimizers import SGD
#随机梯度下降，Keras 中还有一些其他优化器
# Our examples for an exclusive OR.
#x_train 是二维特征向量表示的训练样本列表
x_train = np.array([[0, 0],
    [0, 1],
    [1, 0],
    [1, 1]])
#y_train 是每个特征向量样本对应的目标输出值
y_train = np.array([[0],
    [1],
    [1],
    [0]])
model = Sequential()
#全连接隐藏层包含10 个神经元
num_neurons = 10
model.add(Dense(num_neurons, input_dim=2))
#input_dim 仅在第一层中使用，后面的其他层会自动计算前一层输出的形状，这个例子中输入的XOR 样本是二维特征向量，因此input_dim 设置为2
model.add(Activation('tanh'))
#输出层包含一个神经元，输出结果是二分类值（0 或1）
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.summary()

#输出层中有10 个从这里开始训练模型权重，分别与第一层的10 个神经元一一对应，再加上1 个偏置权重，所以该层共有11 个权重。
sgd = SGD(lr=0.1)
model.compile(loss='binary_crossentropy', optimizer=sgd,
    metrics=['accuracy'])
#随机猜测
model.predict(x_train)

#从这里开始训练模型
model.fit(x_train, y_train, epochs=100)

#保存训练好的模型
import h5py
model_structure = model.to_json()
#用Keras 的辅助方法将网络结构导出为JSON blob 类型以备后用
with open("basic_model.json", "w") as json_file:
    json_file.write(model_structure)
model.save_weights("basic_weights.h5")
#训练好的权重必须被单独保存。第一部分只保存网络结构。在后面重新加载网络结构时必须对其重新实例化

